<?php

namespace app\common\lib;

use app\common\model\PushServer;
use app\common\model\UserPush;
use think\Log;
use think\queue\Job;
use XznsTools\cache\CacheManage;
use XznsTools\push\Jpush;
use XznsTools\push\Oppo;
use XznsTools\push\Vivo;
use XznsTools\push\Xiaomi;

class TaskJob
{
    protected $pushTokenCacheModel = 'push_token'; //huawei=>5 xiaomi=>4 vivo=>3 oppo=>2 极光=> 1

    /**
     * 推送任务消费
     * @param Job $job
     * @param $jobData
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function task(Job $job, $jobData)
    {
        $jobAttempts = $job->attempts();
        if ($jobAttempts > 10)
        {
            Log::write('队列任务执行超过' . $jobAttempts . '次后失败：【' . json_encode($jobData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】：任务关闭', 'error', true);
            $job->delete();
            return;
        }
        try
        {
            switch ($jobData['task'])
            {
                case 'oppoPush':
                    // $this->oppoPush($job, $jobData['data']);
                    $this->oppoPushAll($job, $jobData['data']);
                    break;
                case 'xiaomiPush':
                    // 小米推送
                    $this->xiaomiPush($job, $jobData['data']);
                    break;
                case 'jpushPush':
                    // 极光推送
                    $this->jpushPush($job, $jobData['data']);
                    break;
                case 'vivoPush':
                    // vivo 推送
                    // $this->vivoPush($job, $jobData['data']);
                    $this->vivoPushAll($job, $jobData['data']);
                    break;
                default:
                    $job->delete();
            }
        }
        catch (\Exception $e)
        {
            if (isset($jobData['task']) && in_array($jobData['task'], ['oppoPush', 'vivoPush'], true))
            {
                Log::write('全推任务失败任务删除：【' . json_encode($jobData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "】错误消息：「{$e->getMessage()}」,文件：{$e->getFile()},行数：{$e->getLine()}", 'error', true);
                $job->delete();
            } else
            {
                Log::write('未知任务：【' . json_encode($jobData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】：任务重发', 'error', true);
                $job->release(60);
            }
        }
    }

    /**
     * oppo 推送
     * @param Job $job
     * @param $data
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function oppoPush(Job $job, $data)
    {
        Log::write('oppo推送开始:推送数据' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
        try
        {
            $config = config('push.oppo');
            $oppoPush = new Oppo($config);
            // 获取 oppo推送token
            $oppoCacheModel = CacheManage::getInstance()->setCacheConfig('push_token', 2);
            $token = $oppoCacheModel->getCache(); // 缓存
            if (!$token)
            {
                $tokenData = $oppoPush->getAccessToken();
                if ($tokenData['success'] && $tokenData['data']['code'] == 0)
                {
                    $token = $tokenData['data']['data']['auth_token'];
                    // token 写入缓存
                    $oppoCacheModel->setCache($token, 'details', false);
                } else
                {
                    Log::write('oppo推送获取accessToken失败：【' . json_encode($tokenData['errorMessage'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】:60秒后重试', 'error', true);
                    return $job->release(60);
                }
            }
            $res = $oppoPush->setData($data)->pushAll($token);

            if ($res['success'] && $res['data']['code'] == 0)
            {
                $msgId = $res['data']['data']['message_id'];
                (new PushServer())->save(['is_oppo' => 1, 'oppo_msg_id' => $msgId], ['id' => $data['push_id']]);
            }
        }
        catch (\Exception $e)
        {
            Log::write('oppo推送失败：【' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】' . $e->getMessage(), 'error', true);
            return $job->release(60);
        }
        Log::write('oppo推送完成', 'log', true);
        $job->delete();
    }

    public function xiaomiPush(Job $job, $data)
    {
        Log::write('xiaomi推送开始:推送数据' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
        try
        {
            $config = config('push.xiaomi');
            $miPush = new Xiaomi($config);
            $res = $miPush->setData($data)->pushAll();
            if ($res['code'] == 0)
            {
                $msgId = $res['data']['id'];
                (new PushServer())->save(['is_xiaomi' => 1, 'xiaomi_msg_id' => $msgId], ['id' => $data['push_id']]);
            }
        }
        catch (\Exception $e)
        {
            Log::write('xiaomi推送失败：【' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】' . $e->getMessage() . '：60秒后重启', 'error', true);
            return $job->release(60);
        }
        Log::write('xiaomi推送完成', 'log', true);
        $job->delete();
    }

    public function jpushPush(Job $job, $data)
    {
        Log::write('极光推送开始:推送数据' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
        try
        {
            $config = config('push.jpush');
            $jPush = new Jpush($config);
            $res = $jPush->setData($data)->push();
            if ($res['http_code'] == 200)
            {
                $msgId = $res['body']['msg_id'];
                (new PushServer())->save(['is_jpush' => 1, 'msg_id' => $msgId], ['id' => $data['push_id']]);
            } else
            {
                Log::write('极光推送失败（code非200）：【' . json_encode($res, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】', 'error', true);
            }
        }
        catch (\Exception $e)
        {
            Log::write('极光推送失败：【' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】' . $e->getMessage() . '：60秒后重启', 'error', true);
            return $job->release(60);
        }

        Log::write('极光推送完成', 'log', true);
        $job->delete();
    }

    public function vivoPush(Job $job, $data)
    {
        Log::write('vivo推送开始:推送数据' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
        try
        {
            $config = config('push.vivo');
            $vivoPush = new Vivo($config);
            // 获取 vivo推送token
            $vivoCacheModel = CacheManage::getInstance()->setCacheConfig('push_token', 3);
            $token = $vivoCacheModel->getCache(); // 缓存
            if (!$token)
            {
                $tokenData = $vivoPush->getAccessToken();
                if ($tokenData['success'] && $tokenData['data']['result'] == 0)
                {
                    $token = $tokenData['data']['authToken'];
                    // token 写入缓存
                    $vivoCacheModel->setCache($token, 'details', false);
                } else
                {
                    Log::write('vivo推送获取accessToken失败：【' . json_encode($tokenData['errorMessage'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】:60秒后重试', 'error', true);
                    return $job->release(60);
                }
            }
            $res = $vivoPush->setData($data)->pushAll($token);

            if ($res['success'] && $res['data']['result'] == 0)
            {
                $msgId = $res['data']['taskId'];
                (new PushServer())->save(['is_vivo' => 1, 'vivo_msg_id' => $msgId], ['id' => $data['push_id']]);
            } else
            {
                Log::write('vivo推送失败:推送数据' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '推送返回信息：' . json_encode($res, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '推送任务关闭', 'error', true);
                return $job->delete();
            }
        }
        catch (\Exception $e)
        {
            Log::write('vivo推送失败：【' . json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '】' . $e->getMessage() . '：60秒后重启', 'error', true);
            return $job->release(60);
        }

        Log::write('vivo推送完成', 'log', true);
        $job->delete();
    }

    /**
     * oppo 批量推送
     * @param Job $job
     * @param $pushParams
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function oppoPushAll(Job $job, $pushParams)
    {
        if ($job->attempts() > 1)
        {
            $job->delete();
        }
        $totalNum = 0;
        $pushNum = 0;
        try
        {
            Log::write(':oppo全推开始:推送数据' . \GuzzleHttp\json_encode($pushParams, JSON_UNESCAPED_UNICODE), 'log', true);
            $config = config('push.oppo');
            $oppoPush = new Oppo($config);
            $token = CacheManage::getInstance()->setCacheConfig($this->pushTokenCacheModel, 2)->getCache(); // 缓存
            if (!$token)
            {
                $tokenData = $oppoPush->getAccessToken();
                if ($tokenData['success'])
                {
                    $token = $tokenData['data']['data']['auth_token'];
                    // token 写入缓存
                    CacheManage::getInstance()->setCacheConfig($this->pushTokenCacheModel, 2)->setCache($token, 'details', false);
                } else
                {
                    Log::write([
                        'push_id' => $pushParams['push_id'] ?? 0,
                        'type' => 2,
                        'code' => 500,
                        'msg' => $tokenData,
                        'title' => $pushParams['title'],
                        'content' => "'" . $pushParams['content'] . "'",
                        'extras' => $pushParams['extras'],
                        'regids' => $pushParams['regids'],
                    ], 'flog_push'); // 记录推送信息
                    Log::write('oppo推送失败；错误消息：' . \GuzzleHttp\json_encode($tokenData) . ',推送消息：' . \GuzzleHttp\json_encode($pushParams), 'error');
                    $this->sendMsg('oppo推送失败', "## oppo推送异常\r\n > 错误消息：" . \GuzzleHttp\json_encode($tokenData));
                    $job->delete();
                    return;
                }
            }

            $userPushModel = (new UserPush());
            $totalNum = $userPushModel->whereIn('status', [0, 1])->where('push_type', 2)->group('regid')->count();
            $lastPage = (int)(ceil($totalNum / 2700));
            $page = 1;
            do
            {
                $regidData = $userPushModel->whereIn('status', [0, 1])->where('push_type', 2)->group('regid')->page($page, 2700)->order('id asc')->column('regid');

                $num = count($regidData);
                for ($i = 0; $i < $num; $i += 900)
                {
                    $regids = array_slice($regidData, $i, 900);
                    try
                    {
                        $res = $oppoPush->setData($pushParams)->batchPushOne($token, $regids);
                        if (!$res['success'])
                        {
                            Log::write('oppo推送失败；错误消息：' . \GuzzleHttp\json_encode($res) . ',推送设备ID：' . \GuzzleHttp\json_encode($regids) . ',推送消息：' . \GuzzleHttp\json_encode($pushParams), 'error');
                            $this->sendMsg('oppo推送失败', "## oppo推送异常\r\n > 错误消息：" . \GuzzleHttp\json_encode($res));
                            $md = ['code' => 500, 'msg' => $res];
                        } else
                        {
                            $md = ['code' => 200, 'msg' => $res];
                        }
                    }
                    catch (\Exception $e)
                    {
                        Log::write('oppo推送失败；错误消息：' . $e->getMessage() . ',推送设备ID：' . \GuzzleHttp\json_encode($regids) . ',推送消息：' . \GuzzleHttp\json_encode($pushParams), 'error');
                        $this->sendMsg('oppo推送失败', "## oppo推送异常\r\n > 错误消息：" . $e->getMessage());
                        $md = ['code' => 500, 'msg' => $e->getMessage()];
                    }
                    Log::write([
                        'push_id' => $pushParams['push_id'] ?? 0,
                        'type' => 2,
                        'code' => $md['code'],
                        'msg' => $md['msg'],
                        'title' => $pushParams['title'],
                        'content' => "'" . $pushParams['content'] . "'",
                        'extras' => $pushParams['extras'],
                        'regids' => $regids,
                    ], 'flog_push'); // 记录推送信息
                }
                $pushNum += $num;// 推送总数
                $progress = sprintf('%.2f', $pushNum / $totalNum * 100);
                Log::write('oppo全推' . $num . "条完成,进度{$progress}%,数量{$pushNum}/{$totalNum},总页数{$lastPage},当前页数{$page}：休息100毫秒", 'log', true);

                $page++;    // 下一页
                usleep(100000);
            } while ($lastPage >= $page);

            (new PushServer())->save(['is_oppo' => 1, 'oppo_push_num' => $pushNum], ['id' => $pushParams['push_id']]);
        }
        catch (\Exception $e)
        {
            Log::write('oppo全推异常：' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】', 'error');
        }

        Log::write("oppo全推结束：总数【{$totalNum}】；推送数量【{$pushNum}】", 'log', true);
        $job->delete();
    }

    /**
     * vivo 批量推送
     * @param Job $job
     * @param $pushParams
     */
    public function vivoPushAll(Job $job, $pushParams)
    {
        if ($job->attempts() > 1)
        {
            $job->delete();
        }
        $totalNum = 0;
        $pushNum = 0;
        try
        {
            Log::write(':vivo全推开始:推送数据' . \GuzzleHttp\json_encode($pushParams, JSON_UNESCAPED_UNICODE), 'log', true);
            $pushType = 3;
            $config = config('push.vivo');
            $vivoPush = new Vivo($config);
            $token = CacheManage::getInstance()->setCacheConfig($this->pushTokenCacheModel, $pushType)->getCache(); // 缓存
            if (!$token)
            {
                $tokenData = $vivoPush->getAccessToken();
                if ($tokenData['success'] && $tokenData['data']['result'] == 0)
                {
                    $token = $tokenData['data']['authToken'];
                    // token 写入缓存
                    CacheManage::getInstance()->setCacheConfig($this->pushTokenCacheModel, $pushType)->setCache($token, 'details', false);
                } else
                {
                    Log::write([
                        'push_id' => $pushParams['push_id'] ?? 0,
                        'type' => 3,
                        'code' => 500,
                        'msg' => $tokenData,
                        'title' => $pushParams['title'],
                        'content' => "'" . $pushParams['content'] . "'",
                        'extras' => $pushParams['extras'],
                        'regids' => $pushParams['regids'],
                    ], 'flog_push'); // 记录推送信息
                    Log::write('vivo推送失败；错误消息：' . \GuzzleHttp\json_encode($tokenData) . ',推送消息：' . \GuzzleHttp\json_encode($pushParams), 'error');
                    $this->sendMsg('vivo推送失败', "## vivo推送异常\r\n > 错误消息：" . \GuzzleHttp\json_encode($tokenData));
                    $job->delete();
                    return;
                }
            }

            $userPushModel = (new UserPush());
            $totalNum = $userPushModel->whereIn('status', [0, 1])->where('push_type', $pushType)->group('regid')->count();
            $lastPage = (int)(ceil($totalNum / 2700));
            $page = 1;
            do
            {
                $regidData = $userPushModel->whereIn('status', [0, 1])->where('push_type', $pushType)->group('regid')->page($page, 2700)->order('id asc')->column('regid');

                $num = count($regidData);
                for ($i = 0; $i < $num; $i += 900)
                {
                    $regids = array_slice($regidData, $i, 900);
                    try
                    {
                        if (count($regids) > 1)
                        {   // 批量单推
                            $res = $vivoPush->setData($pushParams)->batchPushOne($token, $regids);
                        } else
                        {  //单推
                            $res = $vivoPush->setData($pushParams)->pushOne($token, current($regids));
                        }
                        if (!$res['success'] || $res['data']['result'] != 0)
                        {
                            Log::write('vivo推送失败；错误消息：' . \GuzzleHttp\json_encode($res) . ',推送设备ID：' . \GuzzleHttp\json_encode($regids) . ',推送消息：' . \GuzzleHttp\json_encode($regids), 'error');
                            $this->sendMsg('vivo推送失败', "## vivo推送异常\r\n > 错误消息：" . \GuzzleHttp\json_encode($res));
                            $md = ['code' => 500, 'msg' => $res];
                        } else
                        {
                            $md = ['code' => 200, 'msg' => $res];
                        }
                    }
                    catch (\Exception $e)
                    {
                        Log::write('vivo推送失败；错误消息：' . $e->getMessage() . ',推送设备ID：' . \GuzzleHttp\json_encode($regids) . ',推送消息：' . \GuzzleHttp\json_encode($pushParams), 'error');
                        $this->sendMsg('vivo推送失败', "## vivo推送异常\r\n > 错误消息：" . $e->getMessage());
                        $md = ['code' => 500, 'msg' => $e->getMessage()];
                    }
                    Log::write([
                        'push_id' => $pushParams['push_id'] ?? 0,
                        'type' => 3,
                        'code' => $md['code'],
                        'msg' => $md['msg'],
                        'title' => $pushParams['title'],
                        'content' => "'" . $pushParams['content'] . "'",
                        'extras' => $pushParams['extras'],
                        'regids' => $regids,
                    ], 'flog_push'); // 记录推送信息
                }
                $pushNum += $num;// 推送总数
                $progress = sprintf('%.2f', $pushNum / $totalNum * 100);
                Log::write('vivo全推' . $num . "条完成,进度{$progress}%,数量{$pushNum}/{$totalNum},总页数{$lastPage},当前页数{$page}：休息100毫秒", 'log', true);

                $page++;    // 下一页
                usleep(100000);
            } while ($lastPage >= $page);

            (new PushServer())->save(['is_vivo' => 1, 'vivo_push_num' => $pushNum], ['id' => $pushParams['push_id']]);
        }
        catch (\Exception $e)
        {
            Log::write('vivo全推异常：' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】', 'error');
        }

        Log::write("vivo全推结束：总数【{$totalNum}】；推送数量【{$pushNum}】", 'log', true);
        $job->delete();
    }

    public function sendMsg($title, $contentMsg)
    {
        $time = time() * 1000;
        $sec = 'SECcd9cc7f9e863073d9ebd0a2d0c9ed2f38d38ffcd67b700866d97ab3f919eb419';
        $sign = urldecode(base64_encode(hash_hmac('sha256', $time . "\n" . $sec, $sec, true)));
        $url = "https://oapi.dingtalk.com/robot/send?access_token=b93977081e77642ab22db0e369abc12e08389a99441b3cc2ed6b3f675d8e9d7e&timestamp={$time}&sign={$sign}";

        Util::sendDingMsg($url, $title, $contentMsg);
    }

    /**
     * 作废
     * @param Job $job
     * @param $pushParams
     */
    public function oppoPushAll1(Job $job, $pushParams)
    {
        Log::write('oppo推送 添加批量推送 开始:推送数据' . json_encode($pushParams, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
        $page = 1;
        $userPushModel = (new UserPush());
        $totalNum = 0;
        do
        {
            try
            {
                // oppo推送用户
                $data = $userPushModel->where(['push_type' => 2])
                    ->whereIn('status', [1, 0])
                    ->group('regid')
                    ->order('id asc')
                    ->page($page, 1000)
                    ->column('regid', 'uid');

                // 本次添加推送人数
                $num = count($data);

                if ($num < 1000)
                {
                    $num = 0;
                }

                if ($data)
                {
                    $regids = array_filter(array_values($data));
                    $pushData = [
                        'type' => 2,
                        'title' => $pushParams['title'],
                        'content' => $pushParams['content'],
                        'extras' => $pushParams['extras'],// ios端打开APP时link使用empty不弹窗，所以修改成1111     empty
                        'regids' => $regids,
                        'thing_type' => 2,
                        'push_id' => $pushParams['push_id'],    //  system_timer_job 推送任务表
                    ];

                    $totalNum += count($regids);

                    Util::queuePush('push_service', $pushData);
                }
            }
            catch (\Exception $e)
            {
                Log::write("oppo推送 添加批量推送 异常:当前推送数据：「" . \GuzzleHttp\json_encode($pushParams, JSON_UNESCAPED_UNICODE) . "」当前page:{$page},异常消息「{$e->getMessage()}」文件：{$e->getFile()}行数：{$e->getLine()}", 'error', true);
            }
            $page++;
            usleep(500000);
        } while ($num > 0);

        (new PushServer())->save(['is_oppo' => 1, 'oppo_push_num' => $totalNum], ['id' => $pushParams['push_id']]);
        Log::write("oppo推送 添加批量推送 结束:共添加推送用户:{$totalNum},推送数据" . json_encode($pushParams, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);

        $job->delete();
    }

    /**
     * 作废
     * @param Job $job
     * @param $pushParams
     */
    public function vivoPushAll1(Job $job, $pushParams)
    {
        Log::write('vivo推送 添加批量推送 开始:推送数据' . json_encode($pushParams, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
        $page = 1;
        $userPushModel = (new UserPush());
        $totalNum = 0;
        do
        {
            try
            {
                // vivo推送用户
                $data = $userPushModel->where(['push_type' => 3])
                    ->whereIn('status', [1, 0])
                    ->group('regid')
                    ->order('id asc')
                    ->page($page, 1000)
                    ->column('regid', 'uid');

                // 本次添加推送人数
                $num = count($data);

                if ($num < 1000)
                {
                    $num = 0;
                }

                if ($data)
                {
                    $regids = array_filter(array_values($data));

                    $pushData = [
                        'type' => 3,
                        'title' => $pushParams['title'],
                        'content' => $pushParams['content'],
                        'extras' => $pushParams['extras'],// ios端打开APP时link使用empty不弹窗，所以修改成1111     empty
                        'regids' => $regids,
                        'thing_type' => 2,
                        'push_id' => $pushParams['push_id'],    //  system_timer_job 推送任务表
                    ];
                    $totalNum += count($regids);

                    Util::queuePush('push_service', $pushData);
                }
            }
            catch (\Exception $e)
            {
                Log::write("vivo推送 添加批量推送 异常:当前推送数据：「" . \GuzzleHttp\json_encode($pushParams, JSON_UNESCAPED_UNICODE) . "」当前page:{$page},异常消息「{$e->getMessage()}」文件：{$e->getFile()}行数：{$e->getLine()}", 'error', true);
            }
            $page++;
            usleep(500000);
        } while ($num > 0);

        (new PushServer())->save(['is_vivo' => 1, 'vivo_push_num' => $totalNum], ['id' => $pushParams['push_id']]);
        Log::write("vivo推送 添加批量推送 结束:共添加推送用户:{$totalNum},推送数据" . json_encode($pushParams, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);

        $job->delete();
    }
}
